﻿#pragma once

#include "../scheduler/scheduler.hh"
#include "../util/box_std_allocator.hh"
#include <memory>
#include <queue>
#include <unordered_map>

namespace kratos {
namespace service {

class ServiceBox;
class SchedulerImpl;

/// Timer type
enum class Type_ {
  NONE = 0,
  ONCE = 1, ///< 一次性定时器
  LOOP, ///< 循环定时器
};

/// Timer callback
using SchedulerCallback = std::function<bool(std::uint64_t, std::time_t)>;

/// Timer node
struct ScheduleNode {
  std::time_t trigger_time{std::time(0)}; ///< 触发时间戳，毫秒
  Type_       type{Type_::NONE};                ///< 定时器类型
  std::time_t duration{std::time(0)};     ///< 持续时间，毫秒
  bool operator<(const ScheduleNode &node);
  std::uint64_t user_data{std::uint64_t(0)}; ///< 用户数据
  std::uint64_t id{std::uint64_t(0)};        ///<  定时器ID
  SchedulerCallback cb;                      ///< 定时器回调
  bool deleted{false};                       ///< 删除标记
  std::uint64_t co_id{0};                    ///< 协程ID
  SchedulerImpl *scheduler{nullptr};         ///< 定时器调度器
};

/**
 * 容器比较函数外壳.
 */
struct NodeShell {
  const NodeShell &operator=(const NodeShell &rht) = delete;
  std::shared_ptr<ScheduleNode> node_ptr;
  NodeShell(std::shared_ptr<ScheduleNode> ptr);
  NodeShell();
  NodeShell(const NodeShell &rht);
  NodeShell(NodeShell &&rht) noexcept;
  const NodeShell &operator=(NodeShell &&rht) noexcept;
  bool operator<(const NodeShell &rht) const noexcept;
  operator bool() noexcept;
};

/**
 * 定时器调度器实现.
 */
class SchedulerImpl : public Scheduler {
  using TimerQueue = std::priority_queue<
      NodeShell,
      std::vector<
        NodeShell,
        kratos::service::Allocator<NodeShell>
      >
  >;
  using TimerMap = std::unordered_map<
      std::uint64_t,
      std::weak_ptr<ScheduleNode>,
      std::hash<std::uint64_t>,
      std::equal_to<std::uint64_t>,
      kratos::service::Allocator<
        std::pair<const std::uint64_t, std::weak_ptr<ScheduleNode>>
      >
  >;
  ServiceBox*   box_{nullptr};  ///< 服务容器
  TimerQueue    queue_;         ///< Timer queue
  std::uint64_t timer_id_{1};   ///< 定时器ID池
  TimerMap      timer_map_;     ///< 定时器表
  std::size_t   co_count_{0};   ///< 协程定时器数量
  std::size_t   noco_count_{0}; ///< 非协程定时器数量

public:
  SchedulerImpl(ServiceBox *box);
  virtual ~SchedulerImpl();
  virtual auto do_schedule(std::time_t ms, std::size_t max_count)
      -> std::size_t override;
  virtual auto schedule(std::time_t duration, SchedulerCallback cb,
                        std::uint64_t udata) -> std::uint64_t override;
  virtual auto schedule_co(std::time_t duration, SchedulerCallback cb,
                           std::uint64_t udata) -> std::uint64_t override;
  virtual auto schedule_many(std::time_t duration, SchedulerCallback cb,
                             std::uint64_t udata) -> std::uint64_t override;
  virtual auto schedule_co_many(std::time_t duration, SchedulerCallback cb,
                                std::uint64_t udata) -> std::uint64_t override;
  virtual auto cancel(std::uint64_t) -> bool override;
  virtual auto size() -> std::size_t override;
  virtual auto size_co() -> std::size_t override;
  virtual auto size_noco() -> std::size_t override;

private:
  /**
   * 获取一个超时的定时器.
   *
   * \param ms 当前时间戳
   * \return 超时的定时器
   */
  NodeShell pop(std::time_t ms);
  /**
   * 将定时器从定时器表删除.
   * 
   * \param id 定时器ID
   */
  void delete_from_map(std::uint64_t id);
};

} // namespace service
} // namespace kratos
